package com.wenqiner.requesttool.view;

import cn.hutool.core.text.CharSequenceUtil;
import com.wenqiner.requesttool.core.business.lingyin.LingyinConstant;
import com.wenqiner.requesttool.core.business.lingyin.ParamData;
import com.wenqiner.requesttool.core.constants.GlobalConstant;
import com.wenqiner.requesttool.core.exception.CustomException;
import com.wenqiner.requesttool.core.fx.AlertTool;
import com.wenqiner.requesttool.core.thread.FixedTaskExecutor;
import com.wenqiner.requesttool.core.thread.ParallelThreadPoolTool;
import com.wenqiner.requesttool.core.thread.ThreadTemplate;
import com.wenqiner.requesttool.core.tool.PlaceholderData;
import com.wenqiner.requesttool.domain.HttpResponseData;
import com.wenqiner.requesttool.domain.OriginRequestJsonData;
import com.wenqiner.requesttool.domain.ParallelStatistics;
import com.wenqiner.requesttool.util.*;
import com.wenqiner.requesttool.util.file.CsvFileParser;
import com.wenqiner.requesttool.view.base.AbstractRequestLayout;
import com.wenqiner.requesttool.view.base.CommonController;
import com.wenqiner.requesttool.view.base.InnerParamProcess;
import com.wenqiner.requesttool.view.base.ParallelController;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.text.TextFlow;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author rhf
 * @date 2023-02-28
 **/
public class ParallelInnerRequestController extends AbstractRequestLayout implements CommonController, InnerParamProcess, ParallelController {
    public static final Logger log = LoggerFactory.getLogger(ParallelInnerRequestController.class);

    private String requestPath;
    private Map<String, String> headerMap;
    private Map<String, String> paramMap;
    private FixedTaskExecutor<String> taskExecutor;
    /**
     * 请求序号
     */
    private AtomicInteger requestNum = new AtomicInteger(0);
    /**
     * 正在请求
     */
    private final AtomicBoolean onRequest = new AtomicBoolean(false);


    private ConcurrentHashMap<String, String> responseMap;

    private ParallelThreadPoolTool parallelThreadPoolTool;

    @FXML
    protected GridPane paramTable;
    @FXML
    protected GridPane headerTable;
    @FXML
    protected TextField paramUrlId;
    @FXML
    private TextField resultUrlId;
    @FXML
    private TextField extractFieldId;
    @FXML
    private TextField extractFieldNameId;

    @FXML
    protected TextArea requestOriginalData;
    @FXML
    protected ComboBox<String> protocolType;
    @FXML
    protected TextField requestUrl;
    @FXML
    protected TextArea resultAnalyseId;
    @FXML
    protected TextField currNumId;
    @FXML
    protected TextField currTimeId;
    @FXML
    protected TextField loopSize;

    @FXML
    protected TextFlow textFlowOut;

    @FXML
    protected ScrollPane scrollPaneOut;

    @FXML
    protected CheckBox runErrorContinue;

    /**
     * Platform.runLater()主线程更新UI
     *
     * @author rhf
     */
    @Override
    public void init() {
        taskExecutor = new FixedTaskExecutor<>(t -> {
            String info = requestNum.incrementAndGet() + "." + t + "\n";
            TextField textField = new TextField(info);
            textField.setEditable(false);
            textField.setPrefWidth(680);
            textField.getStyleClass().add("copyable-label");
            Platform.runLater(() -> {
                ObservableList<Node> children = textFlowOut.getChildren();
                if (children.size() > GlobalConstant.SHOW_PARALLEL_OUT_MAX_SIZE) {
                    children.remove(0, 1);
                }
                children.add(textField);
                scrollPaneOut.setVvalue(1D);
            });
        });
    }

    @FXML
    protected void addParamRowsEvent(ActionEvent event) throws IOException {
        super.createTable(paramTable, super.gridPaneNextRowNum(paramTable));
    }

    @FXML
    protected void addHeaderRowsEvent(ActionEvent event) throws IOException {
        super.createTable(headerTable, super.gridPaneNextRowNum(headerTable));
    }

    @FXML
    protected void paramFileChooserAction(ActionEvent event) {
        FileChooser fileChooser = new FileChooser();
        fileChooser.getExtensionFilters()
                .addAll(new FileChooser.ExtensionFilter("文件", "*.csv"));
        File file = fileChooser.showOpenDialog(new Stage());
        paramUrlId.setText(file.getPath());
    }

    @FXML
    protected void sendRequest(ActionEvent event) throws IOException {
        if (onRequest.getAndSet(Boolean.TRUE)) {
            AlertTool.showInformation("正在发送请求....");
            return;
        }
        resultAnalyseId.setText("正在发送请求....");

        //并发数
        int currNum = Integer.parseInt(currNumId.getText());
        responseMap = new ConcurrentHashMap<>(currNum * 4 / 3 + 1);
        //获取全部系统参数
        paramMap = TableUtil.parseHeader(paramTable);
        //获取全部header
        headerMap = TableUtil.parseHeader(headerTable);
        //获取地址
        requestPath = String.join("", protocolType.getValue(), requestUrl.getText());
        //获取请求参数
        String text = requestOriginalData.getText();
        if (CharSequenceUtil.isBlank(text)) {
            AlertTool.showError(" 请输入请求报文");
            return;
        }
        String platformNo = headerMap.get(LingyinConstant.PARTNER_ID);
        if (CharSequenceUtil.isBlank(platformNo)) {
            AlertTool.showError(LingyinConstant.PARTNER_ID + " is required.");
            return;
        }
        try {
            platformNo = ParsePlaceholderUtil.parseMethodOuter(platformNo);
            headerMap.put(LingyinConstant.PARTNER_ID, platformNo);
        } catch (Exception e) {
            AlertTool.showError("系统函数调用错误");
            return;
        }
        String publicKey = paramMap.get(LingyinConstant.PLATFORM_PUBLIC_KEY);
        if (CharSequenceUtil.isBlank(publicKey)) {
            AlertTool.showError(LingyinConstant.PLATFORM_PUBLIC_KEY + " is required.");
            return;
        }

        //线程执行
        new ThreadTemplate().getCustomerThread(() -> {
            try {
                parallelThreadPoolTool = new ParallelThreadPoolTool();
                PlaceholderData placeholderData = new PlaceholderData(new CsvFileParser());
                OriginRequestJsonData requestJsonData = placeholderData.paramReplace(paramUrlId.getText(), headerMap, text);

                int size = Integer.parseInt(loopSize.getText());
                size = -1 == size ? Integer.MAX_VALUE : size;

                ParallelStatistics parallelStatistics = parallelThreadPoolTool.parallelRun(currNum, this::request, requestJsonData, Long.parseLong(currTimeId.getText()), size);
                //有无导出地址
                String resultUrl = resultUrlId.getText();
                if (CharSequenceUtil.isNotBlank(resultUrl)) {
                    placeholderData = new PlaceholderData(new CsvFileParser());
                    exportField(placeholderData, resultUrl);
                }
                Platform.runLater(()->{
                    resultAnalyseId.setText(
                            printResInfo(
                                    parallelStatistics));
                });
            } catch (Exception e) {
                log.error("请求异常,error msg:[{}],\ne:{}", e.getMessage(), e);
                resultAnalyseId.setText("发送请求失败，请检查后重试");
            } finally {
                onRequest.set(Boolean.FALSE);
            }
        }, Thread.NORM_PRIORITY).start();

    }

    @FXML
    protected void terminationSendRequest(ActionEvent event) {
        closeRequest();
    }


    @FXML
    protected void paramFormatEvent(ActionEvent event) {
        String oriReqStr = requestOriginalData.getText();
        requestOriginalData.setText(CommonUtil.formatJson(oriReqStr));
    }

    @FXML
    protected void clearStatisticsEvent(ActionEvent event) {
        resultAnalyseId.setText("");
    }

    @FXML
    protected void clearOutEvent(ActionEvent event) {
        textFlowOut.getChildren()
                .clear();
        requestNum.set(0);
    }

    /**
     * 组装参数发送http请求
     *
     * @param requestJsonData 请求数据
     * @param index           索引
     * @author rhf
     */
    @Override
    public void request(OriginRequestJsonData requestJsonData, Integer index) {
        OriginRequestJsonData.RequestData requestData = requestJsonData.acquire(index);
        Map<String, String> map = requestData.getHeaderMap();
        String requestJson = requestData.getBody();
        Thread thread = Thread.currentThread();
        map.put(LingyinConstant.REQUEST_ID, requestId());
        try {
            String publicKey = paramMap.get(LingyinConstant.PLATFORM_PUBLIC_KEY);
            HttpResponseData<String> httpResponseData = HttpUtil.doPost(requestPath, map, requestEncrypt(publicKey, requestJson));
            ParamData<String> paramData = responseDecrypt(publicKey, httpResponseData);
            if (paramData.isSuccess()) {
                String s = paramData.getData();
                responseMap.put(thread.getName(), s);
                String format = MessageFormat.format("[http请求成功]\t耗时:{0},参数:{1}"
                        , httpResponseData.getConnectTime()
                        , s);
                taskExecutor.push(format);
                log.info("并发请求，http[请求成功],请求耗时:[{}ms],响应报文:[{}]", httpResponseData.getConnectTime(), s);
            } else {
                String format = MessageFormat.format("[http请求失败],code:{0},请求耗时:[{1}ms],errorMsg:{2}"
                        , paramData.getResponseCode()
                        , httpResponseData.getConnectTime()
                        , paramData.getResponseMsg());
                taskExecutor.push(format);
                log.info("并发请求，http[请求失败],请求耗时:[{}ms],响应报文:[{}]"
                        , httpResponseData.getConnectTime()
                        , ObjectMapperUtil.getMapper().writeValueAsString(paramData));
                if (!runErrorContinue.isSelected())
                {
                    closeRequest();
                }
                throw new CustomException("并发请求，http业务异常");
            }
        } catch (Exception e) {
            log.error("发送http请求失败,error msg:[{}],\ne:{}", e.getMessage(), e);
            throw new CustomException(MessageFormat.format("线程{0}:发送http请求失败", thread.getName()));
        }
    }

    /**
     * 导出文件
     *
     * @param placeholderData 生成文件工具对象
     * @param filePath        文件路径
     * @author rhf
     */
    @SuppressWarnings("all")
    private void exportField(PlaceholderData placeholderData, String filePath) {
        new ThreadTemplate().getCustomerThread(() ->
                        placeholderData.writeToResult(filePath
                                , extractFieldId.getText()
                                , extractFieldNameId.getText()
                                , responseMap)
                , Thread.MIN_PRIORITY).start();
    }

    private void closeRequest() {
        if (null != parallelThreadPoolTool) {
            parallelThreadPoolTool.shutdown();
        }
        onRequest.set(Boolean.FALSE);
    }
}
